/*
    Speedbump aka Project Kimbula
    Copyright (C) 2010  Ping Shin Ching

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses.

	Ping Shin Ching
	righteous.ninja@gmail.com OR ping_sc@hotmail.com
	More information at http://www.tidytutorials.com/p/speedbump.html
 */


#ifndef inmemorycache_h
#define inmemorycache_h

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>

template <typename T, typename Y>
class InMemoryCacheBinaryPredInterface{
public:
	virtual bool operator()(T&, Y&)=0;
};
template <typename T>
class InMemoryCacheUnaryPredInterface{
public:
	virtual bool operator()(T&)=0;
};


template <typename T, typename G>
class InMemoryCache{
	public:
		InMemoryCache(int, int);
		~InMemoryCache();
		int Attach();
		int Detach();
		int ReplaceFirstIf(T&, bool(*f)(T&, T&) );
		//int ReplaceAt(int);
		int FindFirstIf(T&, bool(*f)(T&, T&) );
		int FindFirstIf(T&, InMemoryCacheBinaryPredInterface<T, G>*);
		int Add(T&);
		int RemoveCache();
		int CreateCache();
	private:
		T* memory;
		int currentcount;
		void* shmmem;
		unsigned int size;
		int key;
		InMemoryCache(){}
};

template <typename T>
InMemoryCache<T>::InMemoryCache(int k, int s): size(s), key(k){
	currentcount=0;
}

template <typename T>
InMemoryCache<T>::~InMemoryCache(){

}

/*************************************************************************
 * Attach the memory to the current process
 *************************************************************************/
template <typename T>
int InMemoryCache<T>::CreateCache(){
	int shmid;
	
	if((shmid = shmget(key, sizeof(T)*size, 0666| IPC_CREAT))==-1){
		printf("Error  create %d\n", errno);fflush(stdout);
		return errno;

	}
}

/*************************************************************************
 * Attach the memory to the current process
 *************************************************************************/
template <typename T>
int InMemoryCache<T>::Attach(){
	int shmid;
	
	if((shmid = shmget(key, sizeof(T)*size, 0666 | IPC_CREAT))==-1){
		printf("Error attach shmget %d\n", errno);fflush(stdout);
		return errno;

	}

	if((shmmem = shmat(shmid, 0, 0))==(void*)-1){
		printf("Error atach shmat %d\n", errno);fflush(stdout);
		return errno;
	}

	memory = (T*)shmmem;
	return 0;

}

/*************************************************************************
 * Detach the memory
 *************************************************************************/
template <typename T>
int InMemoryCache<T>::Detach(){
	if(shmdt(shmmem)==0)
		return 0;
	else
		return errno;
}


/*************************************************************************
 * Add an item
 *************************************************************************/
template <typename T>
int InMemoryCache<T>::Add(T& t){
	if(currentcount<size){
		memcpy(&memory[currentcount], &t, sizeof(T));
		currentcount++;
		return 0;
	}
	return -1;
}


/*************************************************************************
 * Replace items if the predicate is true
 *************************************************************************/
template <typename T>
int InMemoryCache<T>::ReplaceFirstIf(T& t, bool(*f)(T&, T&) ){
	int i;
	int shmid;
	for(i = 0 ; i < currentcount ; i++){
		if(f(memory[i], t)){
			memcpy(&memory[i], &t, sizeof(T));
			return 1;
		}
	}
	return 0;
}


/*************************************************************************
 * Find the first item
 *************************************************************************/
template <typename T>
int InMemoryCache<T>::FindFirstIf(T& t, bool(*f)(T&, T&)){
	for(int i = 0 ; i < currentcount ; i++){
		if(f(memory[i], t)){
			memcpy(&t, &memory[i], sizeof(T));
			return 1;
		}
	}
	return 0;
}
template <typename T>
int InMemoryCache<T>::FindFirstIf(T& t, InMemoryCacheBinaryPredInterface<T>* y){
	for(int i = 0 ; i < currentcount ; i++){
		if(y->operator()(memory[i], t)){
			memcpy(&t, &memory[i], sizeof(T));
			return 1;
		}
	}
	return 0;
}

/*************************************************************************
 * Remove Cache
 *************************************************************************/
template <typename T>
int InMemoryCache<T>::RemoveCache(){
	int shmid;
	
	if((shmid = shmget(key, sizeof(T)*size, 0666))==-1){
		printf("Error remove shmget %d\n", errno);fflush(stdout);
	}
	else{
		if(shmctl(shmid, IPC_RMID, 0) == -1){
			printf("Error remove shmctl %d\n", errno);fflush(stdout);
		}
	}
}

#endif
